package edu.study.user.aop;

import edu.study.api.common.dto.BaseReqDto;
import edu.study.api.common.dto.BaseRespDto;
import edu.study.api.common.json.JsonUtils;
import edu.study.common.exception.BizException;
import edu.study.common.validation.RequestValidator;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
@Order(2) // @Order(i)注解来标识切面的优先级。i的值越小，优先级越高
@Slf4j
public class BizImplProxy {

	@Pointcut("execution(* edu.study.user.controller.*ApiImpl.*(..))")
	public void bizPointcut() {
	}

	@SuppressWarnings("rawtypes")
	@Around("bizPointcut()")
	public Object doArount(ProceedingJoinPoint point) throws Throwable {
		long startTime = System.currentTimeMillis();

		Signature signature = point.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();
		String className = methodSignature.getDeclaringType().getSimpleName();
		String methodName = method.getName();
		String resultType = method.getReturnType().getSimpleName();
		Class returnClz = method.getReturnType();
		Object resultResp = null;
		try {
			doBefore(point.getArgs(), className, methodName);
			resultResp = point.proceed();
		} catch (Exception e) {
			if(e instanceof BizException){
				BizException ex = (BizException) e;
				resultResp = genResultObj(returnClz, ex.getCode(), ex.getMessage());
				log.error("<=={}.{} end, 返回={}; 异常信息={}", className, methodName, resultResp, e);
			} else {
				log.error("<=={}.{} end, 返回={}; 异常信息={}", className, methodName, resultResp, e);
			}
		}
		doAfter(resultResp, className, methodName, startTime);
		return resultResp;
	}

	/**
	 * 校验入参并打印
	 */
	protected void doBefore(Object[] args, String className, String methodName) {
		if (args == null || args.length == 0) {
			log.info("==>{}.{} begin, req=null", className, methodName);
			return;
		}
		if(args[0] instanceof BaseReqDto && args.length==1) {
			BaseReqDto req = (BaseReqDto) args[0];
			log.info("==>{}.{} begin, req={}", className, methodName, req);
			RequestValidator.validate(req);
		} else {
			StringBuilder sb = new StringBuilder("==>").append(className).append(".").append(methodName).append(" begin, params=");
			for (int i = 0; i < args.length; i++) {
				sb.append(args[i]);
			}
			log.info(sb.toString());
		}
	}

	/**
	 * 生成返回对象
	 * 
	 * @param target
	 * @param errorCode
	 * @param errMsg
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	private Object genResultObj(Class target, String errorCode, String errMsg) throws Exception {
		if (target == null) {
			return null;
		}
		Object tmp = target.newInstance();
		if (tmp instanceof BaseRespDto) {
			BaseRespDto ret = (BaseRespDto) tmp;
			ret.setResultCode(errorCode);
			ret.setResultMessage(errMsg);
			return ret;
		}
		return tmp;
	}

	/**
	 * 方法结束后打印返回对象
	 */
	protected void doAfter(Object resp, String className, String methodName, long startTime) {
		if (resp != null) {
			String str = JsonUtils.toJson(resp);
			if (str.length() < 1000) {
				log.info( "<=={}.{} end, resp={}, 方法共用时:{}s", className, methodName, resp, (System.currentTimeMillis()-startTime)/1000);
			} else {
				log.info("<=={}.{} end, resp={}...方法共用时:{}s", className, methodName, str.substring(0, 1000), (System.currentTimeMillis()-startTime)/1000);
			}
		} else {
			log.info("<=={}.{} end, resp=null, 方法共用时:{}s", className, methodName, (System.currentTimeMillis()-startTime)/1000);
		}
	}
}